﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL, Software Libre	*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

using ESF.General;

using AjaxControlToolkit;
using System.Web.UI.HtmlControls;
using System.Web;
using System.Web.Configuration;
using System.IO;
using System.Threading;

#endregion


namespace ESF.Web
{
	public static class PageHelper
	{
		#region Constantes

		public const string KEY_SETDOCUMENTDOMAIN = "_DD";

		#endregion


		#region HttpRuntime

		public static string MainURL
		{
			get
			{
				string mainURL;
				

				mainURL = ReadConfig.MainURL;

				if (string.IsNullOrEmpty (mainURL))
				{
					mainURL = HttpContext.Current.Request.ApplicationPath;
				}

				return mainURL;
			}
		}


		public static int MaxRequestLengthBytes
		{
			get
			{
				HttpRuntimeSection runtimeSec;


				runtimeSec = (HttpRuntimeSection) WebConfigurationManager.OpenWebConfiguration("~").GetSection("system.web/httpRuntime");

				// Retorna el valor por defecto
				if (runtimeSec == null) { return 4096000; }

				return runtimeSec.MaxRequestLength * 1024;
			}
		}			

		#endregion


		#region FindControl

		public static C FindControlRecursiveChildren<C>(this Control pControl, string pFindControlID) where C : Control
		{
			if (pControl == null) return null;

			//try to find the control at the current level
			C ctrl = pControl.FindControl(pFindControlID) as C; 

			if (ctrl == null)
			{
				//search the children
				foreach (Control child in pControl.Controls)
				{
					ctrl = FindControlRecursiveChildren <C>(child, pFindControlID);
					if (ctrl != null) break;
				}
			}

			return ctrl;
		}


		public static C FindControlRecursiveParent<C>(this Control pControl, string pFindControlID) where C : Control
		{
			if (pControl == null) return null;

			//try to find the control at the current level
			C ctrl = pControl.FindControl(pFindControlID) as C;

			if (ctrl == null)
			{
				return FindControlRecursiveParent<C>(pControl.Parent, pFindControlID);
			}

			return ctrl;
		}

		#endregion


		#region Propiedades

		public static string PageDomain
		{
			get
			{
				int pointIndex;

				string defaultDomain;


				pointIndex = HttpContext.Current.Request.Url.Host.IndexOf(".");

				if (pointIndex == -1)
				{
					defaultDomain = HttpContext.Current.Request.Url.Host;
				}
				else
				{
					defaultDomain = HttpContext.Current.Request.Url.Host.Substring(pointIndex + 1);
				}

				return defaultDomain;
			}
		}

		#endregion


		#region Presentacion

		public static void PreparePage (Page pPage)
		{
			PreparePage (pPage, PageDomain);	
		}


		public static void PreparePage (Page pPage, string pDomain)
		{
			PreparePage(pPage, PageDomain, true);	
		}


		public static void PreparePage (Page pPage, string pDomain, bool pIncludeCSS)
		{
			PreparePage(pPage, PageDomain, pIncludeCSS, true);	
		}

		
		public static void PreparePage (Page pPage, string pDomain, bool pIncludeCSS, bool pIncludeJS)
		{
			string title;


			pPage.Trace.Write("ESF.Web.PageHelper.PreparePage: Start");

			title = ReadConfig.GetAppSetting ("SiteName", string.Empty);

			if (!string.IsNullOrEmpty (title) && !string.IsNullOrEmpty (pPage.Title) && !pPage.Title.StartsWith (title))
			{
				pPage.Title = title + ": " + pPage.Title;
			}

			if (!pPage.IsCallback)
			{
				if (ToolkitScriptManager.GetCurrent (pPage) != null)
				{
					ToolkitScriptManager.GetCurrent (pPage).AsyncPostBackError += scmManager_AsyncPostBackError;
				}

				//if (!string.IsNullOrWhiteSpace(pDomain))
				//{
					//JS.AddBeginJS(pPage, "if (document.domain != '" + pDomain + "') { try { document.domain = '" + pDomain + "'; } catch (ex) { document.domain = '" + HttpContext.Current.Request.Url.Host + "'; } }");
				//}

				AddAppConfigJSVars(pPage); 

				JS.AddBeginJSReference(pPage, MainURL + "/CommonFiles/loading.js");

				if (pIncludeJS)
				{
					JS.AddBeginJSReference(pPage, MainURL + "/CommonFiles/jquery-1.7.2.min.js");
					JS.AddBeginJSReference(pPage, MainURL + "/CommonFiles/basic.js");
				}

				if (pIncludeCSS)
				{
					PageHelper.AddCSSReference(pPage, ReadConfig.MainURL + "/Css/forms.css");
					PageHelper.AddCSSReference(pPage, ReadConfig.MainURL + "/Css/masterPage.css");
				}
			}

			pPage.Trace.Write("ESF.Web.PageHelper.PreparePage: End");
		}


		private static void scmManager_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
		{
			(sender as ScriptManager).AsyncPostBackErrorMessage = Log.Error(e.Exception).Message;
		}


		public static void HideIfEmpty (params Label [] pLabels)
		{
			if (pLabels == null || pLabels.Length == 0) { return; }

			foreach (Label l in pLabels) { l.Visible = !TextHelper.IsEmpty (l.Text); }
		}

		#endregion


		#region Configuracion del Cache

		#region Static

		public static void SetCacheStatic (Page pPage)
		{
			SetCacheMaxAge (pPage, TimeSpan.FromDays (360));
		}

		#endregion


		#region Meses

		public static void SetCacheBySemester(Page pPage)
		{
			SetCacheByMonthPeriod (pPage, 6);
		}


		public static void SetCacheByTrimester (Page pPage)
		{
			SetCacheByMonthPeriod(pPage, 3);
		}


		public static void SetCacheBimester(Page pPage)
		{
			SetCacheByMonthPeriod(pPage, 2);
		}


		public static void SetCacheOnemester(Page pPage)
		{
			SetCacheByMonthPeriod(pPage, 1);
		}


		private static void SetCacheByMonthPeriod (Page pPage, int pMonthPeriod)
		{
			DateTime now;


			now = new DateTime (DateTime.Now.Year, DateTime.Now.Month, 1);
			
			switch (pMonthPeriod)
			{
				// con 1 uno es en este mismo mes
								
				case 2:					
					// se busca el siguiente fin de bimestre
					// si no es un mes par
					if (now.Month % 2 != 0)
					{
						now = now.AddMonths(1);
					}
					
				break;
				
				case 3:
					// se busca el siguiente fin de trimestre
					if (now.Month <= 3)
					{
						now = now.AddMonths (3 - now.Month);
					}
					else if (now.Month <= 6)
					{
						now = now.AddMonths(6 - now.Month);
					}
					else if (now.Month <= 9)
					{
						now = now.AddMonths(9 - now.Month);
					}
					else
					{
						now = now.AddMonths(12 - now.Month);
					}
				break;

				case 6:
				// se busca el siguiente fin de semestre
					if (now.Month <= 6)
					{
						now = now.AddMonths(6 - now.Month);
					}
					else
					{
						now = now.AddMonths(12 - now.Month);
					}
				break;
			}

			now = now.AddDays (DateTime.DaysInMonth (now.Year, now.Month) - now.Day);

			SetCacheMaxAge (pPage, now, true, false);
		}


		#region Por Siguiente Mes

		public static void SetCacheByTwoMonths(Page pPage, int pDayInMonth)
		{
			SetCacheByMonth(pPage, 2, 2, pDayInMonth);
		}


		public static void SetCacheByOneMonth(Page pPage, int pDayInMonth)
		{
			SetCacheByMonth(pPage, 1, 1, pDayInMonth);
		}


		public static void SetCacheNextMonth(Page pPage)
		{
			SetCacheNextMonths (pPage, 1);
		}


		public static void SetCacheNextTwoMonths(Page pPage)
		{
			SetCacheNextMonths(pPage, 2);
		}


		public static void SetCacheNextThreeMonths(Page pPage)
		{
			SetCacheNextMonths(pPage, 3);
		}


		public static void SetCacheNextSixMonths(Page pPage)
		{
			SetCacheNextMonths(pPage, 6);
		}


		public static void SetCacheNextMonths(Page pPage, int pNextCount)
		{
			SetCacheMaxAge (pPage, DateTime.Now.AddMonths (pNextCount), true, false);
		}


		public static void SetCacheByMonth(Page pPage, int pMonth, int pMonthUnit, int pDayInMonth)
		{
			SetCacheMaxAge (pPage, new DateTime (DateTime.Now.Year, pMonth, pDayInMonth), true, pMonthUnit, false, 1);
		}

		#endregion

		#endregion


		#region Dias

		public static void SetCacheLastDayOfMonth(Page pPage)
		{
			SetCacheDayOfMonth (pPage, DateTime.DaysInMonth (DateTime.Now.Year, DateTime.Now.Month));
		}


		public static void SetCacheDayOfMonth(Page pPage, int pDayOfMonth)
		{
			SetCacheDayOfMonth (pPage, pDayOfMonth, 23, 59, 59);
		}


		public static void SetCacheDayOfMonth(Page pPage, int pDayOfMonth, int pHour, int pMinute, int pSecond)
		{
			DateTime expires;


			expires = DateTime.Now;
			expires = new DateTime (expires.Year, expires.Month, pDayOfMonth, pHour, pMinute, pSecond);

			SetCacheMaxAge (pPage, expires, true, false);
		}

		#endregion


		#region Week

		public static void SetCacheLastDayOfWeek(Page pPage)
		{
			SetCacheDayOfWeek (pPage, DayOfWeek.Friday);
		}

		
		public static void SetCacheDayOfWeek(Page pPage, DayOfWeek pDayOfWeek)
		{
			SetCacheDayOfWeek (pPage, pDayOfWeek, 23, 59, 59);
		}


		/// <summary>
		/// Define el cache de pagina en el cliente y servidor como publico
		/// Maneja el <paramref name="pDayOfWeek"/> para determinar respecto al dia de hoy cuantos minutos faltan para que se deba actualizar
		/// La actualizacion para ese dia puede ser definida por una hora especifica
		/// <example>Hoy es martes a las 3pm (1500), <paramref name="pDayOfWeek"/>es viernes con en 16,00,00 como hora, minuto segundo
		/// por lo tanto falta todo el martes (9), el miercoles (24), el jueves (24) y el viernes (16) hasta las 1600, es decir 73 horas
		/// </example> 
		/// </summary>
		public static void SetCacheDayOfWeek(Page pPage, DayOfWeek pDayOfWeek, int pHour, int pMinute, int pSecond)
		{
			DateTime expires;


			expires = DateTime.Now;
			
			// se calcula que dia es el dia de la semana en esta semana y mes
			// si estamos antes o ahora, sumamos la diferencia en dias, si es para el viernes y es martes faltan ~ 2 dias
			if (expires.DayOfWeek <= pDayOfWeek)
			{
				expires.AddDays (pDayOfWeek - expires.DayOfWeek);
			}
			// estamos despues, adicionamos la diferencia para completar la semana mas el dia que corresponde
			// si es para el jueves y es sabado faltan casi 6 dias
			else
			{
				expires.AddDays ((7 - (int) expires.DayOfWeek) + (int) pDayOfWeek);
			}

			expires = new DateTime(expires.Year, expires.Month, expires.Day, pHour, pMinute, pSecond);

			SetCacheMaxAge(pPage, expires, false, true);
		}

		#endregion 


		#region Definir Cache

		public static void SetCacheMaxAge (Page pPage, DateTime pRelativeDateTime, bool pUseNextMonth, bool pUseNextDay)
		{
			SetCacheMaxAge (pPage, pRelativeDateTime, pUseNextMonth, 1, pUseNextDay, 1);
		}


		public static void SetCacheMaxAge (Page pPage, DateTime pRelativeDateTime, bool pUseNextMonth, int pNextMonthUnit, bool pUseNextDay, int pNextDayUnit)
		{
			DateTime now;


			now = DateTime.Now;

			// Estamos Antes o Ahora
			if (DateTime.Compare (now, pRelativeDateTime) <= 0)
			{
				SetCacheMaxAge (pPage, pRelativeDateTime.Subtract (now));
			}
			// Estamos Despues, se busca la siguiente
			else if (pUseNextMonth)
			{
				pRelativeDateTime = pRelativeDateTime.AddMonths(pNextMonthUnit);
				SetCacheMaxAge (pPage, pRelativeDateTime, pUseNextMonth, pUseNextDay);
			}
			else if (pUseNextDay)
			{
				pRelativeDateTime = pRelativeDateTime.AddDays(pNextDayUnit);
				SetCacheMaxAge(pPage, pRelativeDateTime, pUseNextMonth, pUseNextDay);
			}
			else 
			{
				pRelativeDateTime = pRelativeDateTime.AddHours(1);
				SetCacheMaxAge(pPage, pRelativeDateTime, pUseNextMonth, pUseNextDay);
			}
		}


		public static void SetCacheMaxAge (Page pPage, TimeSpan pDelta)
		{
			pPage.Response.Cache.SetCacheability (HttpCacheability.Public);
			pPage.Response.Cache.SetMaxAge (pDelta);
			pPage.Response.Cache.SetValidUntilExpires(true);
		}


		public static void SetNoCache (Page pPage)
		{
			SetNoCache (pPage.Response);
		}


		public static void SetNoCache(HttpResponse pResponse)
		{
			pResponse.Cache.SetCacheability(HttpCacheability.NoCache);
			pResponse.Cache.SetExpires (DateTime.Now);
		}

		#endregion

		#endregion


		#region Carpetas

		public static string GetVirtualPath (string physicalPath)
		{
			return GetVirtualPath (physicalPath, true);
		}

		public static string GetVirtualPath (string physicalPath, bool pThrowEx)
		{
			return GetVirtualPath (physicalPath, null, pThrowEx);
		}


		public static string GetVirtualPath(string physicalPath, string pRelativeFolderFile, bool pThrowEx)
		{
			if (TextHelper.IsEmpty (physicalPath)) { return null; }


			if (HttpContext.Current == null || HttpContext.Current.Request == null)
			{
				throw new Exception ("There is no HttpContext or Request defined");
			}


			if (!physicalPath.StartsWith (HttpContext.Current.Request.PhysicalApplicationPath, StringComparison.OrdinalIgnoreCase))
			{				
				if (string.IsNullOrEmpty (pRelativeFolderFile))
				{
					if (pThrowEx)
					{
						throw new InvalidOperationException (string.Format ("Physical path {0} is not within the application root {1}", physicalPath, HttpContext.Current.Request.PhysicalApplicationPath));
					}
					else
					{
						return string.Empty;
					}
				}
				else
				{
					return (pRelativeFolderFile + "/" + Path.GetFileName (physicalPath)).Replace ("//", "/");
				}
			}

			return "~/" + physicalPath.Substring (HttpContext.Current.Request.PhysicalApplicationPath.Length).Replace( "\\", "/" );
		}


		public static string GetVirtualPathComplete (string pPhysicalPath, bool pThrowEx)
		{
			string temp;


			temp = GetVirtualPath (pPhysicalPath, pThrowEx);

			if (string.IsNullOrEmpty (temp)) { return string.Empty; }

			return temp.Replace ("~", HttpContext.Current.Request.ApplicationPath);
		}


		public static string GetVirtualPathFull (string physicalPath)
		{
			if (TextHelper.IsEmpty (physicalPath)) { return null; }


			if (HttpContext.Current == null || HttpContext.Current.Request == null)
			{
				throw new Exception ("There is no HttpContext or Request defined");
			}


			if (!physicalPath.StartsWith (HttpContext.Current.Request.PhysicalApplicationPath, StringComparison.OrdinalIgnoreCase))
			{				
				throw new InvalidOperationException (string.Format ("Physical path {0} is not within the application root {1}", physicalPath, HttpContext.Current.Request.PhysicalApplicationPath));
			}
			
			return string.Format ("{0}://{1}:{2}{3}/{4}", 
				HttpContext.Current.Request.Url.Scheme,
				HttpContext.Current.Request.Url.Host,
				HttpContext.Current.Request.Url.Port, 
				HttpContext.Current.Request.ApplicationPath,
				physicalPath.Substring (HttpContext.Current.Request.PhysicalApplicationPath.Length).Replace( "\\", "/" ));
		}

		#endregion


		#region Ajustes

		public static void SetAutocompleteOffChildren (Control pControl)
		{
			if (pControl is TextBox)
			{
				SetAutocompleteOff(pControl as TextBox);
				return;
			}

			foreach (Control control in pControl.Controls)
			{
				SetAutocompleteOffChildren (control);
			}
		}


		public static void SetAutocompleteOffChildren (Page pPage)
		{
			foreach (Control control in pPage.Controls)
			{
				SetAutocompleteOffChildren (control);
			}
		}


		public static void SetAutocompleteOff (params TextBox [] pControls)
		{
			if (DataHelper.IsEmpty (pControls))
			{
				return;
			}

			foreach (TextBox control in pControls)
			{
				control.Attributes ["autocomplete"] = "off";
			}
		}


		public static string ToRelative (Page pPage, string pRelativeURL)
		{
			if (pRelativeURL.StartsWith("~"))
			{
				return pRelativeURL.Replace ("~", pPage.Request.ApplicationPath);
			}

			return pRelativeURL;
		}


		public static void AddCSSReference (Page pPage, string pCSSURL)
		{
			LiteralControl lnkCSS;


			lnkCSS = new LiteralControl(string.Format("<link rel=\"stylesheet\" text=\"text/css\" href=\"{0}\"/>", ToRelative (pPage, pCSSURL)));

			pPage.Header.Controls.Add (lnkCSS);
		}
		
		
		public static string GetApplicationCode (Page pPage)
		{
			string appCode;
				
				
			appCode = null;
			
			if (!pPage.IsPostBack)
			{
				appCode = pPage.Request.QueryString ["ApplicationCode"];
			}
			
			if (TextHelper.IsEmpty (appCode))
			{
				if (pPage.Session != null)
				{
					appCode =  pPage.Session ["ApplicationCode"] as string;
				}	
			}
			
			if (TextHelper.IsEmpty (appCode))
			{
				appCode = ReadConfig.ApplicationCode;
			}
			
			
			SetApplicationCode (pPage, appCode);
			
			return appCode;
		}
		
		
		public static void SetApplicationCode (Page pPage, string pAppCode)
		{
			if (pPage.Session != null)
			{		
				pPage.Session ["ApplicationCode"] = pAppCode;
			}
		}


		public static string GetApplicationName (Page pPage)
		{
			string applicationName = null;
			
			
			if (pPage.Session != null)
			{			
				applicationName = pPage.Session ["ApplicationName"] as string;
			}

			if (string.IsNullOrEmpty (applicationName))
			{
				applicationName = ReadConfig.ApplicationName;
			}

			return applicationName;
		}


		public static void SetApplicationName (Page pPage, string pApplicationName)
		{
			pPage.Session ["ApplicationName"] = pApplicationName;
		}


		public static void AddAppConfigJSVars(Page pPage)
		{
			JS.AddBeginJS(pPage,
				string.Format("mainURL = '{0}';\napplicationCode = '{1}';\napplicationName = '{2}'; mainClientURL = '{3}'; " +
					"CurrencySymbol = '{4}'; CurrencyGroupSeparator = '{5}'; CurrencyDecimalSeparator = '{6}'; CurrencyDecimalDigits = '{7}';",
					ReadConfig.MainURL, GetApplicationCode(pPage), ReadConfig.ApplicationName, ReadConfig.MainClientURL, 
					Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencySymbol,
					Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyGroupSeparator,
					Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator,
					Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalDigits));
		}


		public static object GetRemoveSession (Page pPage, string pKey)
		{
			object result;
			
			
			result = pPage.Session [pKey];

			pPage.Session.Remove (pKey);

			return result;
		}

		#endregion
	}
}
